home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Programmer Disk
/
The Programmer Disk (Microforum).iso
/
xpro
/
tutor
/
pro30
/
chap14.txt
< prev
next >
Wrap
Text File
|
1991-02-04
|
33KB
|
708 lines
Chapter 14
ENCAPSULATION & INHERITANCE
Encapsulation is the cornerstone upon which object oriented
programming is built, and without which it would not exist. We
will cover the topic of encapsulation in this chapter in enough
depth to illustrate its use and what it can do for you in software
development. Because there are many new terms in this chapter, you
could very easily become intimidated, and wish to simply give up
on this new topic. You can be assured that the time spent studying
encapsulation will be greatly rewarded as you apply this new
technique in your software development efforts.
Object oriented programming is not a panacea to solve all of your
software problems, but it is a new and improved way of programming.
In fact it is really more of a software packaging technology than
a new method of programming. You will find that your software will
be easier to write and debug as you gain experience using this new
packaging method. Like any new endeavor however, it will require
some effort on your part to master these concepts.
If you are using an older version of TURBO Pascal you will not be
able to compile and execute the example programs in this chapter.
Versions 5.5 and newer can be used, and as mentioned earlier, it
would be worth your effort to upgrade to a newer version so you can
learn these techniques to improve the quality of your programs.
OUR FIRST ENCAPSULATION
_________________________________________________________________
The example program named ENCAP1.PAS contains our ==============
first example of encapsulation. In order to keep ENCAP1.PAS
it easy to understand, it was kept very short. ==============
This results in a program that does not illustrate
the advantage of using object oriented
programming, but it does give us a start in the right direction.
With this in mind, load ENCAP1.PAS and we will study the code
contained in it.
Line 5 has our first new reserved word, object. This is used in
much the same way that the reserved word record is used, but it has
a much different meaning. An object is permitted to have not only
data embedded within it, but also procedures, functions, and
constructors. Constructors will be described in detail later.
Since data plus procedures and functions can be grouped together
in this fashion, the object is said to be encapsulated. An object
is therefore a group of related data and the subprograms that
operate on that data, all entities being very closely coupled
together.
page 14-1
Chapter 14 - Encapsulation and Inheritance
WHAT IS A METHOD?
_________________________________________________________________
A method is a term used with object oriented programming, and for
the time being we will simply say that a method is either a
function or a procedure (including a constructor). A method is
therefore a method for doing an operation on some data. Lines 8
through 10 are method headers and give the pattern for all calls
to these methods which can be used by the compiler to check for the
correct number and types of parameters.
Once again, we promise to discuss the constructor soon. For the
time being, simply think of it as another procedure.
The entire object type definition is given in lines 5 through 11.
This object contains two variables named length and width, each of
type integer, and three methods which can be used to operate on the
two variables. In the same manner that the definition of a type
in Pascal does not actually give you a variable to use, only a
pattern, the definition of an object type does not give you an
object. We will declare the objects when we get to line 30 of this
program.
You will note that we are already using new terminology, but this
is necessary. The field of object oriented programming has its own
vocabulary and in order for you to understand technical articles
in this field, you must begin now to learn the new terminology.
It won't be too long until you feel somewhat comfortable with it.
THE METHOD IMPLEMENTATION
_________________________________________________________________
The object type definition describes in detail what we can do with
the object but we must now describe what actions will take place
when each of the methods is called. The implementation for each
method will define the operations for that method and are defined
in lines 13 through 28 of this example program. The only thing
that is really different about these methods is the way their
headers are defined, namely the inclusion, in the header, of the
object type name Box dotted to the method name. This is required,
but we will wait until the next example program to define why it
is needed.
The observant student will also notice that we are referring to the
object variables within the methods of that object without the
object name dotted to the variable name. This is because the
implied object name is automatically "with"ed to the variable names
within the method implementations, allowing the variables to be
directly referred to within the objects because of the definition
of object oriented programming. It should be obvious that any
mathematics or logical operations can be done within the
implementations of the methods. In fact, you can perform any legal
Pascal operations within the methods, just like you can in any
page 14-2
Chapter 14 - Encapsulation and Inheritance
Pascal function or procedure. Very short operations were selected
here because we wish to illustrate the interfaces to the methods
at this point in the tutorial.
AN INSTANCE OF AN OBJECT
_________________________________________________________________
We need another new term at this point. When we use the object
type to declare variables of that type as we do in line 30, we are
creating instances of that object type. An instance is like a
variable in conventional Pascal (non object oriented), except that
it can do more and has some very interesting properties that a
simple variable does not have. In line 30 we have created three
instances of the object type named Box and each has two simple
variables associated with it. Three methods are available which
can be called to operate on these variables. We therefore have
three objects named Small, Medium, and Large. In order to
initialize the values stored within the objects we call the three
objects in lines 34 through 36 to store values in their internal
variables by dotting the name of the object to the name of the
method we wish to call. You will note that this looks like the
same technique we use to refer to the fields of a record. We
display the area of the three boxes in lines 38 through 40 using
the same technique used to initialize the values stored, and the
program is complete.
We seem to have accomplished very little with this program that we
could not have more easily accomplished with an even shorter
standard Pascal program, and that is true. This program is only
meant to introduce some of the mechanics of object oriented
programming. Additional programs will be used to illustrate some
of the uses of this new technique.
NEW TERMINOLOGY
_________________________________________________________________
You may note that we switched terminology halfway through the above
paragraphs. We began by referring to the object types as object
types and calling the variables declared in line 30 instances.
Later we began calling the instances objects. In this tutorial we
will refer to the types as object types and the variables either
as objects or instances. This terminology is consistent with
current practice and should help you learn the new terminology.
Another very important point is the fact that we pass a message to
a method rather than call a subprogram as in conventional Pascal.
The difference is rather subtle, but there really is a difference
as we will see a little later in this tutorial.
page 14-3
Chapter 14 - Encapsulation and Inheritance
WHAT DID WE ACCOMPLISH?
_________________________________________________________________
In this program we defined an object type, then declared several
instances of that type, one of which was named Small. The object
named Small has two internal variables that should only be accessed
via its methods, so we will refer to them as private data points.
Actually, they should be unavailable to any user outside of the
method implementations but Borland chose not to make them private
in version 5.5. It is up to you to discipline yourself to not
refer to them directly. (TURBO Pascal version 6.0 has a way to
make the data private. We will study this in the next example
program.) The proper way to use the object is to send a message
to the object telling it to do something to itself. In the case
of the Init method, we are telling it to store the two values in
its private variables named length and width, and in the case of
the Get_Area method, we are telling it to give us the product of
its own internally stored width and length which we can then print
out.
Remember that in the beginning of this chapter we said that object
oriented programming is a code packaging technique. That should
help to explain some of the strange things we did in this program.
As we continue through the example programs, we will see that
everything here was done for a reason and you will eventually learn
to use and prefer object oriented programming methods over the old
familiar procedural programming method you have been using.
Be sure to compile and execute this program to see if it does what
the comments say it will do.
DATA & CODE PROTECTION
_________________________________________________________________
The data and methods are protected from outside influence because
they are packaged together within an object. Of even more
importance is the fact that because they were to be packaged
together, they were probably carefully planned and designed
together during the design stage. This would probably result in
a more understandable program. The object keeps the data and
methods together and keeps them working in close synchronization.
An object type is sometimes referred to as an abstract data type
in the technical literature discussing object oriented programming.
MORE ENCAPSULATION
_________________________________________________________________
The example program named ENCAP2.PAS uses most of the same
techniques as the last program but this is much more meaningful
since it illustrates one of the simplest advantages of using object
oriented programming.
page 14-4
Chapter 14 - Encapsulation and Inheritance
In this program, we define two object types in ==============
lines 5 through 21, Box and Pole. Each has its ENCAP2.PAS
own unique kinds of variables associated with it, ==============
and each has three methods that can be used with
these kinds of data. The method definitions in lines 35 and 46
clearly illustrate why the object name must be associated with the
method name in the method implementation. This allows you to use
the same method name in more than one object definition. In
addition to the two method definitions named Set_Data given here,
we could also define and use another procedure with the name
Set_Data that was a normal Pascal procedure just like any others
we have used in prior chapters of this tutorial. Using the same
method name in several places is often referred to as name
overloading in object oriented programming terminology.
You will note that in lines 5 through 21 we define the object types
which define what each object will do. In lines 23 through 55 we
define the method implementations which define how we do it. It
is assumed that you know enough Pascal at this point to understand
what each method does, so nothing more will be said about the
details of this program except for the private types.
THE PRIVATE TYPE
_________________________________________________________________
Borland added the private type to version 6.0, but it is not as
flexible as it could be. You will notice that the order of
declarations within the objects is significantly different in this
program. Within an object declaration, you are permitted to have
public variables and methods and private variables and methods, but
you are required to list all public entities first. The reserved
word private is then used with all private entities following its
use. Within each section, the variables must come first, followed
by the methods.
Even though the variables are listed as private in these classes,
they are still available to the main program because they are in
the same Pascal unit. If the objects were placed in a different
unit as discussed in chapter 13, the variables defined as private
would be unavailable in the calling program. If you are using
TURBO Pascal 5.5, you will have to remove the word private and
rearrange the variables to put them ahead of the methods in the two
object definitions.
In lines 57 and 58, we declare several objects of the defined
object types and use some of them in the main program. We can
finally illustrate one of the biggest advantages of object oriented
programming.
We should all agree that it would be silly and meaningless to
multiply the height of one of the poles by the width of a box. If
page 14-5
Chapter 14 - Encapsulation and Inheritance
we were using standard procedural programming with all variables
defined globally, it would be a simple matter to accidentally write
height*width and print out the result thinking we had a meaningful
answer. By encapsulating the data within the objects, we would
have to really work at it to get that meaningless answer because
the system itself would prevent us from accidentally using the
wrong data. This is true only if we have agreed not to use any of
the data directly but to do all data access through the available
methods.
Encapsulation is a form of information hiding, but TURBO Pascal has
a rather weak form of it because, as mentioned earlier, Borland
chose not to make the variables within the object private in
version 5.5. In TURBO Pascal 6.0 the variables can be defined as
private variables as mentioned earlier, and are therefore
unaccessible outside of the unit in which they are declared. The
client would be forced to use only the methods provided by the
author of the object to access the contained data. This is true
information hiding and adds some degree of protection to the
internal data.
If you are using TURBO Pascal 5.5, it is up to you to never refer
to the data within the object directly as stated by Borland in the
OOP Guide included with the compiler. Even if you are using
version 6.0, you must still discipline yourself to not refer to the
private data within the defining unit.
The careful student will notice that since all data is carefully
tied up within the objects, inadvertent mixing of the wrong data
is impossible provided a few simple rules are followed as discussed
above. Once again, this is such a small program that it is
difficult to see the advantage of going to all of this trouble.
In a larger program, once the objects are completed, it is a simple
matter to use them knowing that they are debugged and working.
After the data are all printed out, some of the variables are
changed in lines 80 through 82, and the same output statements are
used to reprint the same data so you can observe the changes.
A FEW RULES ARE NEEDED
_________________________________________________________________
As with any new topic, there are a few rules we must follow to use
this new technique. The public variables must all be declared
first in the object followed by the public method definitions. The
reserved word private is then declared followed by the private
variables and finally the private methods. The names of all
variables within an object must be unique and may not be repeated
as the names of any of the formal variables in any of the methods.
Thus length, width, len, and wid must be unique as used in lines
6, 7, 10, and 11. The names of formal variables may be reused in
other methods however, as illustrated in lines 6 and 7, and all
page 14-6
Chapter 14 - Encapsulation and Inheritance
names may be reused in another object. It should be obvious that
all object type names must be unique within a given file and all
objects must have unique names.
All of the above rules are obvious if you spend a little time
thinking about them. They should therefore not be a stumbling
block to anyone with some procedural programming experience.
WHAT IS A CONSTRUCTOR?
_________________________________________________________________
It is time to keep our promise and define just what a constructor
is. In this present context, that of simple objects, the
constructor does very little for us, but we will include one for
nearly every object to illustrate its use. The constructor can be
named anything desired but it would be best to stick with the
convention and name every constructor Init as suggested by Borland.
The constructor is used to initialize all values within an object
and do any other setup that must be done to use an object. The
constructor should be called once for every declared object. When
we get to the topic of virtual functions, constructors will be
absolutely required for every object, but for the simple objects
we are using here, they are optional.
It would be best to include a constructor in every object type,
define the constructor to initialize all variables within the
object, and call the constructor once for each instance of the
object type. Until we get to virtual methods, none of this is
required, but it would be good practice to get in the habit of
doing it.
WHAT IS A DESTRUCTOR?
_________________________________________________________________
A destructor is another method that can be used for cleanup when
you are finished with an object. It is usually used in conjunction
with dynamic allocation to assure that all dynamically allocated
fields associated with the object are deallocated prior to leaving
the scope of the object. A destructor is not illustrated in this
tutorial but it should be easy for you to define and use one when
you have a need for one.
OUR FIRST INHERITANCE
_________________________________________________________________
Load the example program named INHERIT1.PAS for ==============
our first example of a program with inheritance. INHERIT1.PAS
As always, our first encounter with this new topic ==============
will be very simple.
page 14-7
Chapter 14 - Encapsulation and Inheritance
In lines 7 through 14 we define a simple object type defining a
vehicle and a few characteristics about the vehicle. We have the
ability to store a few values and read them out in several ways.
Of course, most of the interest is in the interfaces, so the
implementations are purposely kept very small.
In lines 17 through 35, we declare two additional object types that
use the Vehicle type as a base for the new types as indicated by
the previously defined name Vehicle in parentheses in the object
definitions in lines 17 and 26. The Vehicle object type is said
to be the ancestor type and the two new object types are called
descendant types. The descendant types inherit some information
from the ancestor types according to well defined rules. The
variables in the ancestor type are all included within the
descendant types and are available in objects of the descendant
types just as if they had been defined within the descendant types.
For that reason, all variable names must be unique within the
ancestor type and within each of the descendant types. A name can
be reused in one or more descendants however, as is illustrated in
lines 18 and 27 where the variable name Passenger_Load is used in
both object types.
The method names from the ancestor object types can be repeated in
the descendant object types but this has the effect of overriding
the method of the same name in the ancestor making the ancestor
method unavailable for use in objects of the descendant types.
Objects instantiated of the type Car therefore, have the three
methods available in lines 11 through 13 of the ancestor type, the
constructor in line 19 which overrides the constructor in line 10
of the ancestor type, and the function given in line 22. This
object therefore has five different methods to perform its required
operations.
Objects of type Truck have five methods available also, the two in
lines 11 and 12 and the one in line 33. The two in lines 29 and
34 of the descendant overrides the two in lines 10 and 13 of the
ancestor object type.
You should note that even though some of the methods were
overridden in the descendant object type, they do not affect the
ancestor, and instances of the Vehicle type have two variables and
four methods available.
In effect we have an object hierarchy which can be extended to as
many levels as necessary to complete the task at hand. The most
important part of object oriented programming is the definition of
the objects in a meaningful manner, but it is not something you
will learn to do overnight. It will take a great deal of practice
until you can see the objects in any given project in such a way
that a clear solution can be found. I was somewhat intimidated by
the clever examples found in a classic text on object oriented
programming until I talked to a man that had shared an office with
the author at the time he was writing that particular book. I
learned that what was finally put in the book was at least the
page 14-8
Chapter 14 - Encapsulation and Inheritance
fourth iteration of each problem and in some cases the seventh
before he finally arrived at a good solution. We will have more
to say about this topic as we progress through this tutorial.
HOW DO WE USE THE OBJECTS?
_________________________________________________________________
The implementations of the methods are given in lines 39 through
92 and should be self explanatory, except for a few notable
exceptions. You will note that in line 81 we send a message to the
Vehicle.Init method to initialize some data. A change to the
Vehicle type will be reflected in the Truck type also because of
this call. In lines 64 and 65 we are using some inherited
variables just as if they had been defined as part of the
descendant object types.
In lines 96 through 98 we instantiate one of each and send a
message to their constructors in lines 102 through 104 then print
out a few of the stored values.
Lines 113 through 116 are repeated in lines 120 through 123 where
they are placed within a with section to illustrate that the with
can be used for the calls to the methods in the same manner that
it is used for accessing the fields of a record. Any other details
of this program can be gleaned by the diligent student. Be sure
to compile and execute this program so you can verify the given
result.
WHY USE INHERITANCE?
_________________________________________________________________
Once you have an object that is completely debugged and working,
it is possible that it can be reused on your next project. If you
cannot use it exactly as is, but are required to make a change to
it, you have two choices. You can modify the existing code and
hope you don't introduce any analysis or coding errors, or you can
inherit it into a new object and add code around the completely
debugged object without actually modifying the original code.
Several studies have shown that modifying existing code lead to a
high probability of introducing errors into the modified code that
are difficult to find because you are not as familiar with the code
as you should be. Adding a few methods to an existing object,
however, is not nearly as error prone and is the preferred method.
AN OBJECT IN A UNIT
_________________________________________________________________
Load the example program named VEHICLES.PAS for an example of the
proper way to package the object so it can be conveniently reused
for another project.
page 14-9
Chapter 14 - Encapsulation and Inheritance
The object type definition is given in the public ==============
part of the unit so it is available to any Pascal VEHICLES.PAS
program which needs to use it. The implementation ==============
of the methods are hidden in the implementation
part of the unit where they are not directly available to any
calling program. Note that it is also possible to define a few
local methods within the implementation for use only within the
implementation but none are illustrated here. There is no body to
this unit, only the end statement in line 39 so there is no
initialization code to be executed during loading. It would be
perfectly legal to include an initialization body, but even if you
do, you should be sure to include a constructor to be called once
for each object. This is to prepare you for the use of virtual
functions which we will study in the next chapter.
Referring back to the discussion of the second example program in
this chapter, you will find the means necessary to make the
variables truly private in this program. If you move the two
variables to a location just after the end of the methods, and add
the reserved word private just before the two variables, they will
be truly private and unavailable for direct modification outside
of this unit. If you are using TURBO Pascal 6.0, you should make
this modification and try to access the variables directly in the
calling program which will be discussed next.
You must compile this unit to disk so it can be used with the rest
of the example programs in this chapter.
ANOTHER OBJECT IN A UNIT
_________________________________________________________________
The example program named CARTRUCK.PAS continues ================
the new packaging scheme by including two CARTRUCK.PAS
descendant object types in its interface after ================
telling the system that it uses the Vehicles unit
in line 6.
The remainder of this unit is constructed just like the last one
so nothing more needs to be said about it. Be sure to compile this
unit to disk so it will be available for use with the next example
program.
Note that this unit could have been further divided into two
separate units, one for each object type, but it was felt that it
was important to illustrate that several can be combined in this
manner if desired. In like manner, the last unit could have been
combined with this unit, but once again, it was desired to
illustrate the generality of program decomposition and packaging.
page 14-10
Chapter 14 - Encapsulation and Inheritance
USING THE OBJECTS DEFINED IN UNITS
_________________________________________________________________
Load the program named INHERIT2.PAS for a program ================
that uses the units of the last two example INHERIT2.PAS
programs and is identical to the program named ================
INHERIT1.PAS.
The only difference in these two programs is in the way the code
was packaged. The second way is much more general and more
conducive to good software engineering practices because it allows
separate development of each of the three program units. Each can
be refined independently of the other two and the overall package
can be simpler to debug and maintain. It should be clear that any
changes to the Car object, for example, will be localized to that
single unit and not scattered all over the software terrain.
AN ARRAY AND A POINTER
_________________________________________________________________
Examine the example program named INHERIT3.PAS ================
for an example of the use of a pointer to an INHERIT3.PAS
object and the use of an array of objects. ================
This program is nearly identical to INHERIT2.PAS
except for the addition of an array of Car type objects named
Sedan[1] to Sedan[3], and the definition of a pointer to the Truck
type object named Semi_Point. Lines 16 and 17 illustrate the
initialization of the array of Sedan, and lines 23 through 26
illustrates its use when the data is printed out. An object is
dynamically allocated in line 18 and it is then initialized in the
next line. Its use is illustrated in lines 28 through 40 and it
is deallocated in line 41.
TURBO Pascal 5.5 and newer, have an extension to the New procedure
allowing the dynamic allocation and the initialization to take
place in the same procedure call. The line;
New(Semi_Point, Init(1, 25000.0, 18, 5000.0));
can be used to replace lines 18 and 19 in this program if you
desire to do so.
This program should illustrate that objects can be used with arrays
and pointers in the same manner as a record. Be sure to compile
and execute this program.
WHAT IS MULTIPLE INHERITANCE?
_________________________________________________________________
Multiple inheritance allows the programmer to inherit data and
methods from two or more ancestor objects. When this is done
page 14-11
Chapter 14 - Encapsulation and Inheritance
however, there is a real problem if there are two variables or
methods of the same name and it is up to the programmer to somehow
define which will be used by the descendent. Some object oriented
programming languages allow multiple inheritance, but most do not.
TURBO Pascal has no provision for multiple inheritance, and Borland
has made no indication at this time whether future versions will
permit it.
WHAT SHOULD YOU DO NOW?
_________________________________________________________________
You have reached a major point in your excursion of object oriented
programming, because you now have most of the knowledge you need
to do some serious object oriented programming. The best thing for
you to do at this point is stop studying and get busy programming,
using some of these techniques for your projects. The only topic
left is the use of virtual methods and you can easily defer its use
for a long time.
One point should be made before you begin a serious programming
project. Your first program could have too many objects and be
nearly unreadable unless you strive to use only a few objects.
After you gain experience, you can confidently use more objects
with each programming project. For your first project, define only
a few objects and write the majority of the program in standard
procedural programming methods. Add a few more objects to your
next project and, as you gain experience, you will feel very
comfortable with the use of objects and your programming methods
will be very clear.
Now is the time to begin using this new knowledge but be sure you
enter the water slowly the first time.
PROGRAMMING EXERCISES
_________________________________________________________________
1. Modify ENCAP2.PAS in such a way to multiply the height of the
short pole times the length of the medium box and print the
result out. Even though this is possible to do, it requires
you to expend a bit of effort to accomplish. Remember that
you should not use the components of an object directly, only
through use of the available methods.
2. Add an object named Pick_Up to INHERIT2.PAS of type Truck and
initialize it to some reasonable values. Print out its
loading and efficiency in a manner similar to the Semi.
page 14-12